﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Framework.Verifications;
using Pixysoft.Framework.Remoting;
using System.Threading;
using System.Web;

namespace Pixysoft.Framework.Comet.Core
{
    /// <summary>
    /// remote 兼顾了 local / delegate 不能拆看 因为delegate同样依赖了local
    /// </summary>
    class CometRemoting : ICometRemoting
    {
        public bool Offline(string receiverCode)
        {
            if (string.IsNullOrEmpty(receiverCode))
                throw Exceptions.VerificationFailedException(receiverCode);

            receiverCode = receiverCode.Trim().ToUpper();

            CometRequestPool.Instance.RemoveRequest(receiverCode);

            return true;
        }

        public bool ClearDatas(string senderCode)
        {
            if (!Verification.Verifies(senderCode).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(senderCode);

            senderCode = senderCode.Trim().ToUpper();

            ICometRequest targetrequest = CometRequestPool.Instance.GetRequest(senderCode);

            if (targetrequest == null)
            {
                return true;
            }

            targetrequest.ClearMessages();

            return true;
        }


        public bool IsOnline(string receiverCode)
        {
            if (string.IsNullOrEmpty(receiverCode))
                throw Exceptions.VerificationFailedException(receiverCode);

            receiverCode = receiverCode.Trim().ToUpper();

            return LocalIsOnline(receiverCode);
        }

        private bool LocalIsOnline(string receiverCode)
        {
            ICometRequest request = CometRequestPool.Instance.GetRequest(receiverCode);

            if (request == null)
            {
                return false;
            }

            return request.IsSubscribed;
        }



        /// <summary>
        /// 发送信息 并直到对方接受后，才返回
        /// </summary>
        /// <param name="senderCode"></param>
        /// <param name="receiverCode"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public bool PostMessage(string senderCode, string receiverCode, string message)
        {
            if (!Verification.Verifies(senderCode, receiverCode, message).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(senderCode, receiverCode, message);

            LoggerHelper.Debug("{0}::begin post message, receivercode:{1}, message:{2}", senderCode, receiverCode,
                    message);

            senderCode = senderCode.Trim().ToUpper();

            receiverCode = receiverCode.Trim().ToUpper();

            CometMessage cometmessage = new CometMessage();
            cometmessage.Content = StringCoder.StringToByte(message);
            cometmessage.CreateDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();
            cometmessage.MessageCode = StringHelper.GetMessageCode();
            cometmessage.ModiDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();
            cometmessage.ReceiverCode = receiverCode;
            cometmessage.SenderCode = senderCode;
            cometmessage.TimeStamp = StringHelper.GetTimestamp();

            return PostData(false, cometmessage);
        }

        /// <summary>
        /// 发送一个comet信息  并直到对方接受后，才返回
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public bool PostData(bool persist, ICometMessage message)
        {
            if (message == null)
                throw Exceptions.VerificationFailedException(message);

            return PostDatas(message.ReceiverCode, persist, new ICometMessage[] { message });
        }

        /// <summary>
        /// 发送多个comet信息  并直到对方接受后，才返回
        /// </summary>
        /// <param name="receiverCode"></param>
        /// <param name="persisted"></param>
        /// <param name="messages"></param>
        /// <returns></returns>
        public bool PostDatas(string receiverCode, bool persisted, ICometMessage[] messages)
        {
            if (!Verification.Verifies(receiverCode, messages).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(receiverCode, persisted, messages);

            if (messages == null || messages.Length == 0)
                return false;

            int counter = 0;

            foreach (ICometMessage message in messages)
            {
                if (!Verification.Verifies(message.SenderCode, message.ReceiverCode).IsNotNullOrEmpty().Result())
                    throw Exceptions.UnexpectedResultException("missing sendercode / receivercode");

                message.SenderCode = message.SenderCode.Trim().ToUpper();

                message.ReceiverCode = message.ReceiverCode.Trim().ToUpper();

                message.ModiDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();

                message.TimeStamp = StringHelper.GetTimestamp() + counter++;
            }

            return LocalPostDatas(receiverCode, persisted, messages);
        }

        private bool LocalPostDatas(string receiverCode, bool persisted, params ICometMessage[] messages)
        {
            ICometRequest targetrequest = CometRequestPool.Instance.GetRequest(receiverCode);

            if (targetrequest == null)
            {
                LoggerHelper.Debug("post data(s) fail. receiver is not online. receivercode = {0}", receiverCode);

                return false;
            }

            if (persisted)
            {
                targetrequest.SetPersisted();
            }

            long timestamp = targetrequest.AddMessages(messages);

            if (persisted &&
    Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime().AddSeconds(-1 * SettingHelper.default_web_requestpersisttimeout) > targetrequest.LastPickupDate)
            {
                LoggerHelper.Debug("post data fail in persisted mode. receiver last pickup date is timeout. receivercode = {0}, last pickup date = {1}",
                    targetrequest.SenderCode, targetrequest.LastPickupDate);

                return false;
            }

            return OnCometResponse(targetrequest, timestamp);
        }


        /// <summary>
        /// 等待判断对方是否已经接收 直到超时
        /// </summary>
        /// <param name="request"></param>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        private bool OnCometResponse(ICometRequest request, long timestamp)
        {
            DateTime connectionEndDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime().AddSeconds(
                SettingHelper.default_web_responsetimeout);

            int counter = 0;

            do
            {
                //标志位只有在unsub的时候才会被修改

                if (request.IsDisposed)
                {
                    LoggerHelper.Debug("TO.{0}::target request is disposed, response break from loop, counter:{1}.", request.SenderCode, counter);

                    return false;
                }

                if (request.IsPersisted)
                {
                    LoggerHelper.Debug("TO.{0}::target request is persist, response break from loop, counter:{1}.", request.SenderCode, counter);

                    return true;
                }

                if (Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime() > connectionEndDate)
                {
                    LoggerHelper.Debug("TO.{0}::response connection timeout, globaltime:{1}, connectionenddate:{2}",
                        request.SenderCode,
                        Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime(),
                        connectionEndDate);

                    return false;
                }

                if (request.LastMessageTimeStamp >= timestamp)
                {
                    LoggerHelper.Debug("TO.{0}::response find request is being got return true. timestamp:{1}, lasttimestamp:{2}",
                        request.SenderCode, timestamp, request.LastMessageTimeStamp);

                    return true;
                }

                counter++;

                Thread.Sleep(SettingHelper.default_web_responsewaittime);


            } while (true);

            return false;
        }



        /// <summary>
        /// 批量下载数据 不持久
        /// </summary>
        /// <param name="senderCode"></param>
        /// <returns></returns>
        public ICometMessage[] GetDatas(string senderCode)
        {
            return LocalGetDatas(senderCode, HttpContext.Current);
        }

        public ICometMessage[] LocalGetDatas(string senderCode, HttpContext context)
        {
            if (string.IsNullOrEmpty(senderCode))
                throw Exceptions.VerificationFailedException(senderCode);

            senderCode = senderCode.Trim().ToUpper();


            ICometRequest request = CometRequestPool.Instance.Connect(senderCode, context);

            List<ICometMessage> messages = new List<ICometMessage>();

            if (request.MessageCount > 0)
            {
                messages.AddRange(request.GetAllMessages());
            }

            return messages.ToArray();
        }


        /// <summary>
        /// 获取属于recievercode的comet数据
        /// 如果sendercode = recievercode, 则使用普通comet模式，否则进入broadcast模式
        /// 
        /// </summary>
        /// <param name="senderCode"></param>
        /// <param name="receiverCode"></param>
        /// <returns></returns>
        public ICometMessage GetCometMessage(string senderCode, string receiverCode)
        {
            return LocalGetCometMessage(senderCode, receiverCode, HttpContext.Current);
        }

        public ICometMessage LocalGetCometMessage(string senderCode, string receiverCode, HttpContext context)
        {
            if (!Verification.Verifies(senderCode, receiverCode).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(senderCode, receiverCode);

            senderCode = senderCode.Trim().ToUpper();

            receiverCode = receiverCode.Trim().ToUpper();


            ICometRequest receiverRequest = CometRequestPool.Instance.Connect(receiverCode, context);


            ICometRequest senderRequest = null;

            if (senderCode == receiverCode)
            {
                senderRequest = receiverRequest;
            }
            else
            {
                senderRequest = CometRequestPool.Instance.BroadcastConnect(receiverRequest, senderCode, context);

                if (receiverRequest.IsPersisted)
                    senderRequest.SetPersisted();
            }

            return OnCometRequest(senderRequest, context);
        }


        /// <summary>
        /// 先发送一个message,然后接受receiverCode的信息 不允许持久
        /// 一般使用在同一个链路里面
        /// </summary>
        /// <param name="senderCode"></param>
        /// <param name="receiverCode"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public ICometMessage HoldCometMessage(string senderCode, string receiverCode, ICometMessage message)
        {
            return LocalHoldCometMessage(senderCode, receiverCode, message, HttpContext.Current);
        }

        public ICometMessage LocalHoldCometMessage(string senderCode, string receiverCode, ICometMessage message, HttpContext context)
        {
            if (message == null)
                throw Exceptions.VerificationFailedException(message);

            if (!Verification.Verifies(senderCode, receiverCode, message.SenderCode, message.ReceiverCode).IsNotNullOrEmpty().Result())
                throw Exceptions.UnexpectedResultException("missing sendercode / receivercode");

            LoggerHelper.Debug("{0}::begin hold comet message, receivercode:{1}, message:{2}", senderCode, receiverCode,
                StringCoder.ByteToString(ByteCoder.Instance.SubBytes(message.Content, 0, 100)));

            senderCode = senderCode.Trim().ToUpper();

            receiverCode = receiverCode.Trim().ToUpper();

            message.SenderCode = message.SenderCode.Trim().ToUpper();

            message.ReceiverCode = message.ReceiverCode.Trim().ToUpper();

            message.ModiDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();

            message.TimeStamp = StringHelper.GetTimestamp();

            if (LocalPostDatas(message.ReceiverCode, false, message))
            {
                LoggerHelper.Debug("{0}::local post data succeed.", senderCode);
            }
            else
            {
                LoggerHelper.Debug("{0}::post data fail, return null.", senderCode);

                return null;
            }

            ICometRequest receiverRequest = CometRequestPool.Instance.Connect(receiverCode, context);


            ICometRequest senderRequest = null;

            if (senderCode == receiverCode)
            {
                senderRequest = receiverRequest;
            }
            else
            {
                senderRequest = CometRequestPool.Instance.BroadcastConnect(receiverRequest, senderCode, context);
            }

            return OnCometRequest(senderRequest, context);
        }




        /// <summary>
        /// 进入循环等待comet回复 = Nextmessage
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private ICometMessage OnCometRequest(ICometRequest request, HttpContext context)
        {
            ICometMessage message = null;

            int counter = 0;

            do
            {
                //标志位只有在unsub的时候才会被修改

                if (request.IsDisposed)
                {
                    message = request.GetNextMessage();

                    LoggerHelper.Info("{0}:request is disposed, break from loop, sender:{1} counter:{2} message.count:{3} responsemsg:{4}",
                        request.RequestUid, request.SenderCode, counter, request.MessageCount, message != null);

                    break;
                }

                //如果顾客链接断开 则返回 由于进入了异步模式 HttpContext不存在了。

                if (context != null && !context.Response.IsClientConnected)
                {
                    CometRequestPool.Instance.DisConnect(request);

                    LoggerHelper.Debug("{0}::response.clientconnected is false, disconnect request, break from loop, counter:{1}.", request.SenderCode, counter);

                    break;
                }

                message = request.GetNextMessage();

                if (message != null)
                {
                    LoggerHelper.Debug("{0}:: request get response, sender:{1}, message.length:{2}, content:{3}.",
                        request.SenderCode, message.SenderCode, message.Content.Length,
                        StringCoder.ByteToString(ByteCoder.Instance.SubBytes(message.Content, 0, 100)));

                    //2011-01-12 需要断开链接

                    CometRequestPool.Instance.DisConnect(request);

                    break;
                }

                if (Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime() > request.ConnectionEndDate)
                {
                    LoggerHelper.Debug("{0}::connection timeout, globaltime:{1}, connectionenddate:{2}",
                        request.SenderCode,
                        Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime(),
                        request.ConnectionEndDate);

                    message = ConnectionTimeout(request);

                    break;
                }

                //逻辑上不可能到达这里 因为connection的时间一定比request小 除非用户非法断开了connection

                if (Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime() > request.RequestEndDate)
                {
                    LoggerHelper.Debug("{0}::request timeout, globaltime:{1}, RequestEndDate:{2}",
                        request.SenderCode,
                        Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime(),
                        request.RequestEndDate);

                    message = RequestTimeout(request);

                    break;
                }

                counter++;

                request.Visit(CometRequestPool.Instance);

                Thread.Sleep(SettingHelper.default_web_requestwaittime);


            } while (true);

            return message;
        }

        /// <summary>
        ///  请求超时 直接移除 如果持久模式，则返回最后一个message；否则返回null
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private ICometMessage RequestTimeout(ICometRequest request)
        {
            LoggerHelper.Debug("{0}::request timeout. pickupdate = {1}", request.SenderCode);

            CometRequestPool.Instance.DisConnect(request);

            ICometRequest req = CometRequestPool.Instance.RemoveRequest(request);

            if (!req.IsSubscribed)
                return null;

            if (req.IsPersisted)
            {
                //因为在判断了has message 之后才执行 所以 支持 delegate模式

                ICometMessage msg = req.GetLastMessage();

                LoggerHelper.Debug("{0}::request timeout,in persist mode, return last message:{1}", req.SenderCode, msg != null);

                return msg;
            }

            LoggerHelper.Debug("{0}::request timeout, return null.", req.SenderCode);

            return null;
        }

        /// <summary>
        /// 链接超时 保留内存对象 如果持久模式，则返回最后一个message；否则返回null
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private ICometMessage ConnectionTimeout(ICometRequest request)
        {
            LoggerHelper.Debug("{0}::request connection timeout.pickupdate = {1}", request.SenderCode, request.LastPickupDate);

            CometRequestPool.Instance.DisConnect(request);

            if (request.IsPersisted)
            {
                //因为在判断了has message 之后才执行 所以 支持 delegate模式

                ICometMessage msg = request.GetLastMessage();

                LoggerHelper.Debug("{0}::connection timeout,in persist mode, return last message:{1}", request.SenderCode, msg != null);

                return msg;
            }

            LoggerHelper.Debug("{0}::connection timeout, return null.", request.SenderCode);

            return null;
        }
    }
}
